Skip to content

feat 591 : A .taskboard file for every individual board#643

Merged
tu2-atmanand merged 252 commits into
release-v2.0.0from
feat-591/fileForBoard
May 9, 2026
Merged

feat 591 : A .taskboard file for every individual board#643
tu2-atmanand merged 252 commits into
release-v2.0.0from
feat-591/fileForBoard

Conversation

@tu2-atmanand tu2-atmanand added feature New feature request enhancement An existing feature can be enhanced/improved. future idea New Future or improvement, but not urgent, can be implemented in near future. optimization The algorithm/code can be optimized. A better approach brainstorm These issue/feat needs to be discussed and have to find the solution labels Jan 20, 2026
@github-project-automation github-project-automation Bot moved this to Future Ideas in Task Board Dev Jan 20, 2026
@tu2-atmanand tu2-atmanand linked an issue Jan 20, 2026 that may be closed by this pull request
@tu2-atmanand tu2-atmanand removed the brainstorm These issue/feat needs to be discussed and have to find the solution label May 1, 2026
@tu2-atmanand tu2-atmanand marked this pull request as ready for review May 8, 2026 14:41
@qodo-code-review
Copy link
Copy Markdown
Contributor

Review Summary by Qodo

Implement individual .taskboard files with v2 migration system and comprehensive refactoring

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Implements individual .taskboard files for every board, addressing tickets #561, #591, #554, and
  #723
• Introduces TaskBoardFileManager for managing board configuration persistence and caching with
  debounced saves
• Adds v2 migration system with template board creation on first install and migration notifications
• Refactors drag-drop manager with auto-scroll functionality during drag operations and improved
  error tracking
• Updates settings UI with date format validation using date-fns library and refactored data
  access patterns
• Enhances task content formatting with new date utility functions and improved extraction methods
• Refactors advanced filter components to use board ID-based management instead of leaf/index-based
  approach
• Reorganizes imports across codebase to use explicit .js extensions for ES modules and relative
  paths
• Replaces console logging with bugReporterManagerInsatance.addToLogs() for centralized error
  tracking
• Expands enums with new UI options (ViewInSplitTab, ViewInWindow), task properties, and view
  types
• Improves file scanning to exclude .taskboard files and adds validation for unsupported checkbox
  symbols
• Adds boards explorer and merge boards commands for enhanced board management
• Removes legacy components and board operations utilities in favor of new file-based architecture
Diagram
flowchart LR
  A["Individual Board Files<br/>`.taskboard`"] -->|"TaskBoardFileManager"| B["Board Persistence<br/>& Caching"]
  C["Plugin Main"] -->|"Monkey-patch"| A
  C -->|"v2 Migration"| D["Template Creation<br/>& Notifications"]
  E["Drag-Drop Manager"] -->|"Auto-scroll &<br/>Error Tracking"| F["Enhanced UX"]
  G["Settings UI"] -->|"Date Validation"| H["Improved Config"]
  I["Advanced Filters"] -->|"Board ID-based"| J["Flexible Filtering"]
  B --> K["Board Registry<br/>& Metadata"]
Loading

Grey Divider

File Changes

1. src/managers/DragDropTasksManager.ts ✨ Enhancement +556/-429

Refactor drag-drop manager with auto-scroll and improved error handling

• Refactored import paths to use relative paths with .js extensions for ES modules
• Added auto-scroll functionality during drag operations with startAutoScroll(),
 handleAutoScroll(), and stopAutoScroll() methods
• Updated currentDragDataPayload interface to replace currentBoardIndex with currentViewIndex
 and currentBoardID
• Replaced console.log() and console.error() calls with
 bugReporterManagerInsatance.addToLogs() for better error tracking
• Refactored swimlane change handling to always call updateTaskItemOnSwimlaneChange() without
 conditional checks
• Updated method signatures to pass plugin.settings.data directly instead of
 plugin.settings.data.globalSettings
• Deprecated drop indicator DOM manipulation methods and moved indicator rendering to column
 components
• Changed handleTasksOrderChange() to use taskBoardFileManager.loadBoardUsingID() and
 saveBoard() for board persistence
• Updated task update function calls to include both oldTask and newTask parameters
• Replaced DatePickerModal instantiation with openDateInputModal() helper function

src/managers/DragDropTasksManager.ts


2. src/settings/SettingConstructUI.ts ✨ Enhancement +678/-380

Update settings UI with date validation and refactored data access

• Updated import paths to use relative paths with .js extensions for ES modules
• Changed from i18next to direct t() function import
• Refactored settings data access to use plugin.settings.data directly instead of
 plugin.settings.data.globalSettings
• Added date format validation with date-fns library using format(), parse(), and isValid()
 functions
• Replaced dropdown-based tasks cache file path setting with text input and transfer/reset buttons
• Moved hidden task properties settings section to appear after archived tasks file path setting
• Commented out experimental features toggle and lazy loading settings as deprecated
• Updated tag color type enum values from Text/Background to TagText/TagBg/CardBg
• Improved QuickAdd plugin compatibility setting UI with conditional rendering
• Added comprehensive date and date-time format validation with visual feedback
• Commented out task completion time-related settings

src/settings/SettingConstructUI.ts


3. src/interfaces/Enums.ts ✨ Enhancement +27/-3

Expand enums with new UI options and task properties

• Added ViewInSplitTab and ViewInWindow enum values to EditButtonMode
• Updated TagColorType enum with new values TagText, TagBg, and CardBg (replacing Text and
 Background)
• Added new task property names: FilePathInHeader, ParentFolder, and FullPath to
 taskPropertiesNames
• Added upcoming view types to viewTypeNames: list, table, inbox, and gantt
• Added new enums HeaderUITypeOptions with horizontal and vertical options
• Added new enum viewsPanelPropertiesToShow with properties like Title, Description,
 progress, CreatedDate, and ModifiedDate

src/interfaces/Enums.ts


View more (109)
4. main.ts ✨ Enhancement +748/-530

Major refactor for individual board files and v2 migration system

• Added comprehensive JSDoc comments and reorganized imports with explicit file extensions (.js)
• Implemented TaskBoardFileManager for managing individual .taskboard files and added
 monkey-patching for intercepting .taskboard file clicks
• Enhanced activateView() method to support opening specific board files with filePath parameter
 and duplicate leaf handling
• Added v2 migration system with checkAndNotifyV2MigrationsRequired() and template board creation
 on first install
• Improved file event queue processing with better error handling, progress notices, and filtering
 of allowed file types
• Added findModifiedFilesOnAppAbsense() to detect and process files modified while Obsidian was
 closed
• Refactored settings access from settings.data.globalSettings to settings.data throughout the
 file
• Added new commands for opening boards explorer and merging boards, plus migration modal for v2
 updates

main.ts


5. src/utils/taskLine/TaskContentFormatter.ts ✨ Enhancement +282/-249

Refactor task formatting with new date utilities and improved extraction

• Updated all import paths to use explicit .js extensions and reorganized import statements
• Refactored date formatting to use new formatDateStringAsPerSettings() and
 formatDateTimeAsPerSettings() utility functions
• Changed extractPriority() to return an object with value and parsedString properties instead
 of just a number
• Simplified sanitizeTags() function by removing oldTagsList parameter and improved tag handling
 logic
• Updated settings access from globalSettings nested property to direct settings.data access
• Added proper date extraction functions (extractCreatedDate, extractStartDate, etc.) instead of
 inline regex patterns
• Improved error handling with bugReporterManagerInsatance.addToLogs() instead of console.warn()
• Enhanced completion and cancellation date handling with conditional checks for
 autoAddCompletedDate and autoAddCancelledDate settings

src/utils/taskLine/TaskContentFormatter.ts


6. src/managers/RealTimeScanner.ts ✨ Enhancement +24/-21

Update imports and enhance error handling in RealTimeScanner

• Updated import paths to use explicit .js extensions and reorganized imports
• Added Notice import from obsidian for user feedback
• Replaced console.error() with bugReporterManagerInsatance.addToLogs() for better error
 tracking
• Added notice feedback when files don't get scanned in processAllUpdatedFiles()
• Improved code consistency with proper error handling throughout the class

src/managers/RealTimeScanner.ts


7. src/services/MarkdownHoverPreview.ts Formatting +1/-1

Fix import path with explicit file extension

• Updated import path for TaskBoard to use explicit .js extension and correct relative path

src/services/MarkdownHoverPreview.ts


8. src/managers/VaultScanner.ts ✨ Enhancement +328/-292

Import reorganization and extraction functions refactoring with enhanced error handling

• Reorganized imports to use relative paths with .js extensions and updated from i18next instead
 of local helper
• Added TFolder import and new instance variables testDate and supportedChecklistSymbols for
 enhanced task scanning
• Replaced getCurrentLocalTimeString() with getCurrentLocalDateTimeString() for better date-time
 handling
• Changed multiple extraction functions to return RegExpMatchArray | null instead of strings, with
 proper null-coalescing handling
• Improved error handling by using bugReporterManagerInsatance.addToLogs() instead of
 console.error()
• Enhanced date parsing using date-fns library with parse() and isValid() functions
• Added validation to skip tasks with unsupported checkbox symbols (ticket #737)
• Refactored settings access to remove globalSettings nesting level throughout the file
• Updated fileTypeAllowedForScanning() to exclude TFolder instances and .taskboard files

src/managers/VaultScanner.ts


9. src/managers/TaskBoardFileManager.ts ✨ Enhancement +1289/-0

New TaskBoardFileManager for individual board file management

• New file implementing board configuration management for individual .taskboard files
• Provides methods to load, save, and manage board data with caching and registry tracking
• Includes debounced save functionality to prevent rapid successive writes
• Implements board file validation, creation, deletion, and scanning across vault
• Contains migration framework for handling board data structure changes across plugin versions
• Manages board registry to track board IDs, file paths, names, and descriptions

src/managers/TaskBoardFileManager.ts


10. src/components/AdvancedFilterer/Component.ts ✨ Enhancement +255/-174

Advanced filter component refactoring with board ID-based management

• Renamed class from TaskFilterComponent to AdvancedFilterComponent for clarity
• Updated imports to use i18next and relative paths with .js extensions
• Changed constructor to accept currentBoardID string instead of leafId and activeBoardIndex
• Added conditionsRequiringValue array as class property for date comparison operators
• Enhanced status dropdown to support grouped options with visual separators
• Improved error handling with bugReporterManagerInsatance.addToLogs() instead of console.warn()
• Added validation to filter out invalid filter groups during load to prevent data corruption
• Updated modal references from FilterConfigModal to BoardFiltersStoreModal
• Refactored settings access to remove globalSettings nesting

src/components/AdvancedFilterer/Component.ts


11. src/components/AdvancedFilterer/Modal.ts ✨ Enhancement +25/-21

Advanced filter modal refactoring with board ID-based approach

• Renamed class from ViewTaskFilterModal to AdvancedFilterModal
• Updated imports to use i18next and relative paths with .js extensions
• Changed constructor to accept currentBoardID string instead of leafId and activeBoardIndex
• Updated component instantiation to use AdvancedFilterComponent with new parameters
• Improved error handling by replacing console.error() with
 bugReporterManagerInsatance.addToLogs()

src/components/AdvancedFilterer/Modal.ts


12. .github/copilot-instructions.md Additional files +1/-1

...

.github/copilot-instructions.md


13. README.md Additional files +5/-5

...

README.md


14. backup-data.json Additional files +291/-0

...

backup-data.json


15. data.json Additional files +309/-1168

...

data.json


16. esbuild.config.mjs Additional files +2/-2

...

esbuild.config.mjs


17. manifest.json Additional files +1/-1

...

manifest.json


18. package.json Additional files +10/-9

...

package.json


19. src/components/AddOrEditTaskRC.tsx Additional files +119/-87

...

src/components/AddOrEditTaskRC.tsx


20. src/components/AdvancedFilterer/LoadSavedFiltersModal.ts Additional files +56/-49

...

src/components/AdvancedFilterer/LoadSavedFiltersModal.ts


21. src/components/AdvancedFilterer/Popover.ts Additional files +28/-24

...

src/components/AdvancedFilterer/Popover.ts


22. src/components/AdvancedFilterer/index.ts Additional files +6/-5

...

src/components/AdvancedFilterer/index.ts


23. src/components/KanbanView/Column.tsx Additional files +0/-809

...

src/components/KanbanView/Column.tsx


24. src/components/KanbanView/KanbanBoardView.tsx Additional files +142/-306

...

src/components/KanbanView/KanbanBoardView.tsx


25. src/components/KanbanView/KanbanSwimlanesContainer.tsx Additional files +260/-166

...

src/components/KanbanView/KanbanSwimlanesContainer.tsx


26. src/components/KanbanView/LazyColumn.tsx Additional files +485/-571

...

src/components/KanbanView/LazyColumn.tsx


27. src/components/MapView/CustomNodeResizer.tsx Additional files +1/-1

...

src/components/MapView/CustomNodeResizer.tsx


28. src/components/MapView/EdgeWithToolbar.tsx Additional files +6/-6

...

src/components/MapView/EdgeWithToolbar.tsx


29. src/components/MapView/MapView.tsx Additional files +430/-327

...

src/components/MapView/MapView.tsx


30. src/components/MapView/MapViewMinimap.tsx Additional files +3/-3

...

src/components/MapView/MapViewMinimap.tsx


31. src/components/MapView/ResizableNodeSelected.tsx Additional files +19/-14

...

src/components/MapView/ResizableNodeSelected.tsx


32. src/components/MapView/TasksImporterPanel.tsx Additional files +112/-28

...

src/components/MapView/TasksImporterPanel.tsx


33. src/components/TaskBoardEmbedComponent.tsx Additional files +58/-0

...

src/components/TaskBoardEmbedComponent.tsx


34. src/components/TaskBoardViewContainer.tsx Additional files +1387/-0

...

src/components/TaskBoardViewContainer.tsx


35. src/components/TaskBoardViewContent.tsx Additional files +0/-1048

...

src/components/TaskBoardViewContent.tsx


36. src/components/TaskCard/TaskItem.tsx Additional files +466/-459

...

src/components/TaskCard/TaskItem.tsx


37. src/components/TaskCard/TaskItemV2.tsx Additional files +476/-458

...

src/components/TaskCard/TaskItemV2.tsx


38. src/editor-extensions/task-operations/gutter-marker.ts Additional files +3/-3

...

src/editor-extensions/task-operations/gutter-marker.ts


39. src/editor-extensions/task-operations/property-hiding.ts Additional files +7/-10

...

src/editor-extensions/task-operations/property-hiding.ts


40. src/interfaces/BoardConfigs.ts Additional files +382/-12

...

src/interfaces/BoardConfigs.ts


41. src/interfaces/Constants.ts Additional files +10/-1

...

src/interfaces/Constants.ts


42. src/interfaces/GlobalSettings.ts Additional files +313/-560

...

src/interfaces/GlobalSettings.ts


43. src/interfaces/Mapping.ts Additional files +187/-11

...

src/interfaces/Mapping.ts


44. src/interfaces/TaskItem.ts Additional files +2/-2

...

src/interfaces/TaskItem.ts


45. src/managers/BugReporter.ts Additional files +72/-49

...

src/managers/BugReporter.ts


46. src/modals/AddColumnModal.ts Additional files +29/-38

...

src/modals/AddColumnModal.ts


47. src/modals/AddOrEditTaskModal.tsx Additional files +24/-18

...

src/modals/AddOrEditTaskModal.tsx


48. src/modals/AddViewModal.ts Additional files +105/-0

...

src/modals/AddViewModal.ts


49. src/modals/BoardConfigModal.tsx Additional files +1047/-758

...

src/modals/BoardConfigModal.tsx


50. src/modals/BoardsExplorer.ts Additional files +313/-0

...

src/modals/BoardsExplorer.ts


51. src/modals/BugReporterModal.ts Additional files +21/-18

...

src/modals/BugReporterModal.ts


52. src/modals/ClosePopupConfrimationModal.tsx Additional files +1/-1

...

src/modals/ClosePopupConfrimationModal.tsx


53. src/modals/ConfigureColumnSortingModal.ts Additional files +84/-60

...

src/modals/ConfigureColumnSortingModal.ts


54. src/modals/CustomStatusConfigurator.ts Additional files +23/-39

...

src/modals/CustomStatusConfigurator.ts


55. src/modals/DeleteConfirmationModal.tsx Additional files +1/-1

...

src/modals/DeleteConfirmationModal.tsx


56. src/modals/DiffContentCompareModal.ts Additional files +25/-25

...

src/modals/DiffContentCompareModal.ts


57. src/modals/EditTagsModal.ts Additional files +3/-3

...

src/modals/EditTagsModal.ts


58. src/modals/MergeBoardsModal.ts Additional files +263/-0

...

src/modals/MergeBoardsModal.ts


59. src/modals/ScanFilterModal.ts Additional files +13/-20

...

src/modals/ScanFilterModal.ts


60. src/modals/ScanVaultModal.tsx Additional files +34/-24

...

src/modals/ScanVaultModal.tsx


61. src/modals/SwimlanesConfigModal.tsx Additional files +415/-360

...

src/modals/SwimlanesConfigModal.tsx


62. src/modals/TaskBoardActionsModal.ts Additional files +6/-6

...

src/modals/TaskBoardActionsModal.ts


63. src/modals/TextInputModal.ts Additional files +110/-0

...

src/modals/TextInputModal.ts


64. src/modals/date_picker/DatePickerComponent.ts Additional files +90/-63

...

src/modals/date_picker/DatePickerComponent.ts


65. src/modals/date_picker/DatePickerModal.ts Additional files +16/-13

...

src/modals/date_picker/DatePickerModal.ts


66. src/modals/date_picker/index.ts Additional files +3/-2

...

src/modals/date_picker/index.ts


67. src/modals/date_time_picker/DateTimePickerComponent.ts Additional files +210/-0

...

src/modals/date_time_picker/DateTimePickerComponent.ts


68. src/modals/date_time_picker/DateTimePickerModal.ts Additional files +60/-0

...

src/modals/date_time_picker/DateTimePickerModal.ts


69. src/obsidian_views/AddOrEditTaskView.tsx Additional files +28/-24

...

src/obsidian_views/AddOrEditTaskView.tsx


70. src/obsidian_views/TaskBoardView.tsx Additional files +458/-0

...

src/obsidian_views/TaskBoardView.tsx


71. src/obsidian_views/TaskBoardView_TextFileView.tsx Additional files +427/-0

...

src/obsidian_views/TaskBoardView_TextFileView.tsx


72. src/regularExpressions/TasksPluginRegularExpr.ts Additional files +21/-19

...

src/regularExpressions/TasksPluginRegularExpr.ts


73. src/services/CommunityPlugins.ts Additional files +23/-10

...

src/services/CommunityPlugins.ts


74. src/services/FileSystem.ts Additional files +52/-11

...

src/services/FileSystem.ts


75. src/services/FrontmatterRenderer.ts Additional files +25/-20

...

src/services/FrontmatterRenderer.ts


76. src/services/MarkdownEditor.ts Additional files +2/-2

...

src/services/MarkdownEditor.ts


77. src/services/MarkdownUIRenderer.ts Additional files +14/-12

...

src/services/MarkdownUIRenderer.ts


78. src/services/MultiSuggest.ts Additional files +15/-9

...

src/services/MultiSuggest.ts


79. src/services/OpenModals.ts Additional files +161/-117

...

src/services/OpenModals.ts


80. src/services/subModules.ts Additional files +2/-2

...

src/services/subModules.ts


81. src/services/tasks-plugin/helpers.ts Additional files +32/-38

...

src/services/tasks-plugin/helpers.ts


82. src/settings/2_x_x_Migrations/LegacyInterfacesAndTypings.ts Additional files +127/-0

...

src/settings/2_x_x_Migrations/LegacyInterfacesAndTypings.ts


83. src/settings/2_x_x_Migrations/MigrationModal.tsx Additional files +331/-0

...

src/settings/2_x_x_Migrations/MigrationModal.tsx


84. src/settings/2_x_x_Migrations/MigrationUtils.ts Additional files +750/-0

...

src/settings/2_x_x_Migrations/MigrationUtils.ts


85. src/settings/2_x_x_Migrations/index.ts Additional files +34/-0

...

src/settings/2_x_x_Migrations/index.ts


86. src/settings/SettingSynchronizer.ts Additional files +79/-107

...

src/settings/SettingSynchronizer.ts


87. src/settings/TaskBoardSettingTab.ts Additional files +6/-5

...

src/settings/TaskBoardSettingTab.ts


88. src/taskboardAPIs.ts Additional files +14/-6

...

src/taskboardAPIs.ts


89. src/types/obsidian-ex.d.ts Additional files +3/-2

...

src/types/obsidian-ex.d.ts


90. src/utils/BoardOperations.ts Additional files +0/-24

...

src/utils/BoardOperations.ts


91. src/utils/CheckBoxUtils.ts Additional files +55/-36

...

src/utils/CheckBoxUtils.ts


92. src/utils/DateTimeCalculations.ts Additional files +295/-94

...

src/utils/DateTimeCalculations.ts


93. src/utils/JsonFileOperations.ts Additional files +112/-88

...

src/utils/JsonFileOperations.ts


94. src/utils/MarkdownFileOperations.ts Additional files +36/-25

...

src/utils/MarkdownFileOperations.ts


95. src/utils/TaskItemCacheOperations.ts Additional files +25/-34

...

src/utils/TaskItemCacheOperations.ts


96. src/utils/TaskItemUtils.ts Additional files +36/-23

...

src/utils/TaskItemUtils.ts


97. src/utils/UIHelpers.ts Additional files +11/-26

...

src/utils/UIHelpers.ts


98. src/utils/UserTaskEvents.ts Additional files +160/-135

...

src/utils/UserTaskEvents.ts


99. src/utils/ViewUtils.ts Additional files +251/-0

...

src/utils/ViewUtils.ts


100. src/utils/algorithms/AdvancedFilterer.ts Additional files +70/-36

...

src/utils/algorithms/AdvancedFilterer.ts


101. src/utils/algorithms/ColumnSegregator.ts Additional files +89/-76

...

src/utils/algorithms/ColumnSegregator.ts


102. src/utils/algorithms/ColumnSortingAlgorithm.ts Additional files +14/-14

...

src/utils/algorithms/ColumnSortingAlgorithm.ts


103. src/utils/algorithms/ScanningFilterer.ts Additional files +150/-127

...

src/utils/algorithms/ScanningFilterer.ts


104. src/utils/lang/helper.ts Additional files +14/-24

...

src/utils/lang/helper.ts


105. src/utils/lang/locale/ar.json Additional files +3/-3

...

src/utils/lang/locale/ar.json


106. src/utils/lang/locale/cs.json Additional files +3/-3

...

src/utils/lang/locale/cs.json


107. src/utils/lang/locale/da.json Additional files +3/-3

...

src/utils/lang/locale/da.json


108. src/utils/lang/locale/de.json Additional files +2/-2

...

src/utils/lang/locale/de.json


109. src/utils/lang/locale/en.json Additional files +138/-31

...

src/utils/lang/locale/en.json


110. src/utils/lang/locale/en.ts Additional files +138/-28

...

src/utils/lang/locale/en.ts


111. src/utils/lang/locale/es.json Additional files +2/-2

...

src/utils/lang/locale/es.json


112. Additional files not shown Additional files +0/-0

...

Additional files not shown


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Copy Markdown
Contributor

qodo-code-review Bot commented May 8, 2026

Code Review by Qodo

🐞 Bugs (5) 📘 Rule violations (0) 📎 Requirement gaps (2)

Context used

Grey Divider


Action required

1. Moved file regenerates boardData.id 📎 Requirement gap ≡ Correctness
Description
loadBoardFromDisk() changes a board's id whenever the same boardId exists in the registry
under a different filePath, which can happen after a rename/move. This breaks board identity and
can create duplicate/empty boards when opening the renamed .taskboard file.
Code

src/managers/TaskBoardFileManager.ts[R83-96]

+			const existingRegistryEntry = Object.entries(
+				this.taskBoardFilesRegistry,
+			).find(([, entry]) => entry.boardId === boardData.id);
+
+			if (existingRegistryEntry) {
+				const [, registryEntry] = existingRegistryEntry;
+				if (registryEntry.filePath !== filePath) {
+					// Same boardID but different filePath - generate new ID
+					const oldId = boardData.id;
+					boardData.id = generateRandomTempTaskId();
+					// console.log(
+					// 	`Board ID conflict detected. Changed board ID from "${oldId}" to "${boardData.id}" for file: ${filePath}`,
+					// );
+				}
Evidence
PR Compliance ID 2 requires board identity to survive .taskboard rename/move. The added
conflict-resolution logic regenerates boardData.id when the registry has the same boardId but a
different filePath, and loadBoardUsingPath() calls loadBoardFromDisk() before updating the
registry, so a moved/renamed file can be treated as an ID conflict and get a new ID.

Allow .taskboard files to be renamed/moved without breaking board identity
src/managers/TaskBoardFileManager.ts[83-96]
src/managers/TaskBoardFileManager.ts[211-233]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
When a `.taskboard` file is renamed/moved, `loadBoardFromDisk()` can regenerate `boardData.id` due to a registry `filePath` mismatch, breaking board identity.

## Issue Context
The registry can be stale (still pointing to `oldPath`) at the moment the moved file is opened. In that case, a path mismatch should update the registry entry (or otherwise reconcile), not create a new board ID.

## Fix Focus Areas
- src/managers/TaskBoardFileManager.ts[80-99]
- src/managers/TaskBoardFileManager.ts[211-233]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Map save drops hidden nodes 📎 Requirement gap ≡ Correctness
Description
Map node layout persistence is overwritten with only the currently rendered nodes, so tasks not
present under the current filter/project can have their saved positions deleted. Returning to a
previous filter/project can therefore reset/rearrange node positions.
Code

src/components/MapView/MapView.tsx[R555-568]

+	const handlenodePositionChange = useCallback(() => {
		try {
-			const stored = localStorage.getItem(NODE_POSITIONS_STORAGE_KEY);
-			if (stored) {
-				allBoardPositions = JSON.parse(stored);
-				if (typeof allBoardPositions !== 'object' || allBoardPositions === null) {
-					allBoardPositions = {};
-				}
+			// Update positions for current board with validation
+			const nodesDataMap: Record<string, nodePositionData> = {};
+			for (const node of nodes) {
+				const x = Number.isFinite(node.position?.x) ? node.position.x : 0;
+				const y = Number.isFinite(node.position?.y) ? node.position.y : 0;
+				const width = Number.isFinite(node?.width) ? node.width ?? 300 : 300;
+				nodesDataMap[node.id] = { x, y, width };
			}
-		} catch (error) {
-			console.warn('Failed to load existing positions:', error);
-			allBoardPositions = {};
-		}

-		// Update positions for current board with validation
-		const posMap = nodes.reduce((acc, n) => {
-			const x = Number.isFinite(n.position?.x) ? n.position.x : 0;
-			const y = Number.isFinite(n.position?.y) ? n.position.y : 0;
-			acc[n.id] = { x, y };
-			return acc;
-		}, {} as Record<string, nodePosition>);
-
-		setPositions(posMap);
-		allBoardPositions[String(activeBoardIndex)] = posMap;
-
-		try {
-			localStorage.setItem(NODE_POSITIONS_STORAGE_KEY, JSON.stringify(allBoardPositions));
+			// Only update useRef - no state update needed, avoiding re-render
+			allNodesData.current = nodesDataMap;
+			emitMapDataUpdatedSignal(true);
Evidence
PR Compliance ID 3 requires Map View layout to persist across filter/project switches. The new
handlenodePositionChange() rebuilds nodesDataMap only from the current nodes array and then
replaces allNodesData.current, and SAVE_MAP persists allNodesData.current to the .taskboard
file—so any nodes not currently rendered (e.g., filtered out) are dropped from the persisted layout.

Store Map View layout persistently to prevent layout being trashed when switching filters/projects
src/components/MapView/MapView.tsx[555-568]
src/components/MapView/MapView.tsx[171-186]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Saving map layout currently replaces the persisted `nodesData` with only the nodes visible in the current render, which can erase positions for tasks hidden by filters/projects.

## Issue Context
The Map View should preserve layout for all tasks across context switches. Persisted `nodesData` should be updated as a merge (update known nodes, keep existing entries for hidden nodes), not as a full overwrite.

## Fix Focus Areas
- src/components/MapView/MapView.tsx[555-568]
- src/components/MapView/MapView.tsx[171-186]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Root createFolder empty path 🐞 Bug ≡ Correctness
Description
TaskBoardFileManager.createNewBoardFile() computes folderPath as an empty string for root-level
files and attempts vault.createFolder(""), which can throw and prevent creating a new .taskboard
file. This breaks the “Create Template Board” flow which creates the file at vault root.
Code

src/managers/TaskBoardFileManager.ts[R610-615]

+			const parts = normalizedFilePath.split("/");
+			parts.pop();
+			const folderPath = parts.join("/");
+			if (!(await this.plugin.app.vault.adapter.exists(folderPath))) {
+				await this.plugin.app.vault.createFolder(folderPath);
+			}
Evidence
The template board creation uses a root-level file path (no /), and createNewBoardFile()
unconditionally tries to create the parent folder derived from splitting on /, producing an empty
folderPath and calling createFolder("").

src/obsidian_views/TaskBoardView.tsx[406-421]
src/managers/TaskBoardFileManager.ts[608-615]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Creating a new board file at the vault root can derive an empty parent folder path and call `vault.createFolder("")`, which can throw and block board creation.

### Issue Context
`TaskBoardView.handleCreateTemplateBoard()` creates a root-level file path like `TaskBoard-Template-<ts>.taskboard`, then calls `TaskBoardFileManager.createNewBoardFile()`.

### Fix Focus Areas
- src/managers/TaskBoardFileManager.ts[608-622]
- src/obsidian_views/TaskBoardView.tsx[406-421]

### What to change
- After computing `folderPath`, guard for root-level paths:
 - If `folderPath` is empty (or equals `.`), skip folder existence/creation.
- Consider consistently using `normalizedFilePath` for subsequent writes/caches/registry to avoid path mismatches.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (2)
4. filePath state becomes object 🐞 Bug ≡ Correctness
Description
TaskBoardView.setState() assigns an object to state.filePath instead of a string, corrupting the
view state persisted into Obsidian’s workspace layout. This will break later restores that expect
filePath to be a string and can prevent the board from reopening correctly.
Code

src/obsidian_views/TaskBoardView.tsx[R187-190]

+							state.filePath = {
+								...state.state,
+								filePath: this.currentFilePath
+							};
Evidence
The view’s restore logic explicitly checks typeof filePath === "string", but later overwrites
state.filePath with an object literal, making subsequent restores fail that check and causing the
fallback path to run instead of opening the intended file.

src/obsidian_views/TaskBoardView.tsx[153-191]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`TaskBoardView.setState()` corrupts the state shape by assigning an object to `state.filePath`.

### Issue Context
The view expects `state.filePath` to be a string (it checks `typeof filePath === "string"`). Persisting an object there can break workspace restore behavior.

### Fix Focus Areas
- src/obsidian_views/TaskBoardView.tsx[174-191]

### What to change
- Replace the object assignment with a string assignment, e.g.:
 - `state = { ...state, filePath: this.currentFilePath }`
 - (or `state.filePath = this.currentFilePath` if you intentionally mutate)
- Ensure `state.filePath` remains a plain string in all branches.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Unflushed debounced board saves 🐞 Bug ☼ Reliability
Description
Board mutations are persisted via TaskBoardFileManager.debouncedSaveBoard() (timer-based), but
main.ts does not flush/cancel pending timers on onunload(). If Obsidian/plugin unloads before
the debounce fires, recent board edits can be lost.
Code

main.ts[R209-217]

	onunload() {
		console.log("Task Board : Uninstalling...");

		// deleteAllLocalStorageKeys(); // TODO : Enable this while production build. This is disabled for testing purpose because the data from localStorage is required for testing.
		// onUnloadSave(this.plugin);
+
+		// Obsidian already does this, no need to manually detach.
		// this.app.workspace.detachLeavesOfType(VIEW_TYPE_TASKBOARD);
	}
Evidence
Debounced saves are actively used during board interactions, but unload does not call
clearAllDebouncedSaves() nor any force-save path; timers will not reliably execute after plugin
unload, so the last edits inside the debounce window may never be written to disk.

src/components/KanbanView/KanbanBoardView.tsx[28-41]
src/managers/TaskBoardFileManager.ts[427-471]
main.ts[209-217]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Debounced board saves can be dropped on plugin unload because pending timers are not flushed.

### Issue Context
`debouncedSaveBoard()` uses `setTimeout`. If the app closes/unloads before the timer executes, the final write may not happen.

### Fix Focus Areas
- main.ts[209-217]
- src/managers/TaskBoardFileManager.ts[427-494]

### What to change
- In `TaskBoard.onload()`/`onunload()` (or via `this.register(() => ...)` cleanup), ensure pending saves are handled:
 - Prefer: track dirty boards and `await taskBoardFileManager.forceSaveBoard(...)` for each on unload.
 - At minimum: call `taskBoardFileManager.clearAllDebouncedSaves()` and/or implement a `flushAllDebouncedSaves()` that runs saves immediately.
- Ensure the cleanup is awaited if possible (or use Obsidian-supported async unload hooks if available).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

6. Registry saveSettings not awaited 🐞 Bug ☼ Reliability
Description
TaskBoardFileManager.addNewBoardToRegistry() updates plugin.settings.data.taskBoardFilesRegistry
but calls plugin.saveSettings() without awaiting it. This can cause racing writes and leave the
registry update unpersisted if multiple updates happen quickly or the app closes soon after.
Code

src/managers/TaskBoardFileManager.ts[R549-556]

+			this.plugin.settings.data.taskBoardFilesRegistry = {
+				[boardId]: newEntry,
+				...updatedTaskBoardFilesRegistry,
+			};
+			this.plugin.saveSettings();
+			this.taskBoardFilesRegistry =
+				this.plugin.settings.data.taskBoardFilesRegistry;
+
Evidence
saveSettings() is an async I/O operation (await this.saveData(...)), so fire-and-forget calls
can reorder or be cut off by shutdown; addNewBoardToRegistry() is used as part of board open/load
paths and should persist deterministically.

src/managers/TaskBoardFileManager.ts[549-555]
main.ts[358-365]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Settings persistence for `taskBoardFilesRegistry` is done via an un-awaited async call, allowing races and potential loss of registry updates.

### Issue Context
`TaskBoard.saveSettings()` performs async disk I/O. Calling it without `await` means callers proceed before the write completes.

### Fix Focus Areas
- src/managers/TaskBoardFileManager.ts[508-556]
- main.ts[358-365]

### What to change
- Change `this.plugin.saveSettings();` to `await this.plugin.saveSettings(this.plugin.settings);` (or equivalent) and propagate/handle async where needed.
- Also consider awaiting other internal async calls that depend on the registry being persisted (e.g., callers of `addNewBoardToRegistry`).
- If performance is a concern, introduce a dedicated debounced/queued settings writer with explicit flush on unload.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. Leaf click-path not cleared 🐞 Bug ≡ Correctness
Description
TaskBoardView.setState() prioritizes leaf.taskboardFilePath if present, but this field is never
cleared after use and is populated from the monkey patch using state.state.file (not the
filePath shape used elsewhere). This can cause later setState() calls on the same leaf to
incorrectly treat it as a “clicked file” and load the wrong board.
Code

main.ts[R462-474]

+				function (this: WorkspaceLeaf, state: any, eState?: any) {
+					const isTaskBoardView = state.type === VIEW_TYPE_TASKBOARD;
+					const filePath = state.state?.file as string | undefined;
+					const isTaskboardFile =
+						filePath && filePath.endsWith(".taskboard");
+
+					if (isTaskBoardView && isTaskboardFile) {
+						// Store the file path directly on the leaf instance for immediate access
+						(this as any).taskboardFilePath = filePath;
+
+						// Also set ephemeral state for safety
+						this.setEphemeralState({ taskboardFilePath: filePath });
+					}
Evidence
The monkey patch writes leaf.taskboardFilePath from state.state.file, while normal activations
use state.state.filePath; the view then always prefers leaf.taskboardFilePath when set, and does
not reset it, so the clicked-file code path can override state-driven restoration in subsequent
invocations.

main.ts[457-474]
main.ts[299-308]
src/obsidian_views/TaskBoardView.tsx[133-152]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
A leaf-scoped `taskboardFilePath` is used to decide whether a `.taskboard` file was clicked, but the field is set inconsistently and never cleared, which can override state-based loading.

### Issue Context
- Monkey patch sets `leaf.taskboardFilePath` from `state.state.file`.
- `activateView()` sets `state.state.filePath` (different key).
- `TaskBoardView.setState()` always checks `leaf.taskboardFilePath` first and does not clear it after consuming.

### Fix Focus Areas
- main.ts[457-478]
- main.ts[297-309]
- src/obsidian_views/TaskBoardView.tsx[133-156]

### What to change
- Unify the state key used across the plugin:
 - Either use `state.state.file` everywhere (including `activateView()`), or update the monkey patch to also read `state.state.filePath`.
- In `TaskBoardView.setState()`, after handling a clicked file, clear the leaf field (e.g., `delete (this.leaf as any).taskboardFilePath`) so subsequent `setState()` calls don’t keep taking the clicked-file path.
- Optionally, read from `leaf.getEphemeralState().taskboardFilePath` (already set by `activateView`) instead of a custom leaf property.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread src/managers/TaskBoardFileManager.ts
Comment on lines +555 to +568
const handlenodePositionChange = useCallback(() => {
try {
const stored = localStorage.getItem(NODE_POSITIONS_STORAGE_KEY);
if (stored) {
allBoardPositions = JSON.parse(stored);
if (typeof allBoardPositions !== 'object' || allBoardPositions === null) {
allBoardPositions = {};
}
// Update positions for current board with validation
const nodesDataMap: Record<string, nodePositionData> = {};
for (const node of nodes) {
const x = Number.isFinite(node.position?.x) ? node.position.x : 0;
const y = Number.isFinite(node.position?.y) ? node.position.y : 0;
const width = Number.isFinite(node?.width) ? node.width ?? 300 : 300;
nodesDataMap[node.id] = { x, y, width };
}
} catch (error) {
console.warn('Failed to load existing positions:', error);
allBoardPositions = {};
}

// Update positions for current board with validation
const posMap = nodes.reduce((acc, n) => {
const x = Number.isFinite(n.position?.x) ? n.position.x : 0;
const y = Number.isFinite(n.position?.y) ? n.position.y : 0;
acc[n.id] = { x, y };
return acc;
}, {} as Record<string, nodePosition>);

setPositions(posMap);
allBoardPositions[String(activeBoardIndex)] = posMap;

try {
localStorage.setItem(NODE_POSITIONS_STORAGE_KEY, JSON.stringify(allBoardPositions));
// Only update useRef - no state update needed, avoiding re-render
allNodesData.current = nodesDataMap;
emitMapDataUpdatedSignal(true);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

2. Map save drops hidden nodes 📎 Requirement gap ≡ Correctness

Map node layout persistence is overwritten with only the currently rendered nodes, so tasks not
present under the current filter/project can have their saved positions deleted. Returning to a
previous filter/project can therefore reset/rearrange node positions.
Agent Prompt
## Issue description
Saving map layout currently replaces the persisted `nodesData` with only the nodes visible in the current render, which can erase positions for tasks hidden by filters/projects.

## Issue Context
The Map View should preserve layout for all tasks across context switches. Persisted `nodesData` should be updated as a merge (update known nodes, keep existing entries for hidden nodes), not as a full overwrite.

## Fix Focus Areas
- src/components/MapView/MapView.tsx[555-568]
- src/components/MapView/MapView.tsx[171-186]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread src/managers/TaskBoardFileManager.ts
Comment thread src/obsidian_views/TaskBoardView.tsx
Comment thread main.ts
@tu2-atmanand
Copy link
Copy Markdown
Owner Author

Will merge this PR for now in the main branch of 2.0.0 and will release the first beta version. All these AI suggestion will be worked on later.

@tu2-atmanand tu2-atmanand merged commit 5737608 into release-v2.0.0 May 9, 2026
@github-project-automation github-project-automation Bot moved this from In progress to Ready to Review in Task Board Dev May 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement An existing feature can be enhanced/improved. feature New feature request optimization The algorithm/code can be optimized. A better approach

Projects

Status: Ready to Review

Development

Successfully merging this pull request may close these issues.

FR : A .taskboard file to store each board inside the vault

1 participant